// String Library - by LeeZH
//      A small collection of useful code related to manipulating strings.
//
// If you want this code in any license besides GPLv3 (which the rest of
// the Checkmate source code is under), just ask me at lee@leezh.net.

#include <algorithm>
#include "string-lib.hpp"

using namespace std;

namespace arg
{
    bool check(vector<string> &list, string functionName, unsigned int minimumArgs)
    {
        if (list.size() >= minimumArgs && (*list.begin()) == functionName)
        {
            list.erase(list.begin());
            return true;
        }
        return false;
    }

    bool option1(vector<string> &list, string test)
    {
        vector<string>::iterator i = find_if(list.begin(), list.end(), bind2nd(equal_to<string>(), test));
        if (i != list.end())
        {
            list.erase(i);
            return true;
        }
        return false;
    }

    bool option2(vector<string> &list, string test, string &result)
    {
        vector<string>::iterator i = find_if(list.begin(), list.end(), bind2nd(equal_to<string>(), test));
        if (i != list.end())
        {
            result = "";
            vector<string>::iterator v = i++;
            if (v != list.end())
            {
                result = *v;
                list.erase(v);
            }
            else
            {
                return true;
            }
            list.erase(i);
            return true;
        }
        return false;
    }

    bool final(vector<string> &list, string &result, bool extracheck)
    {
        if (list.size() > 0)
        {
            if (extracheck && list.size() > 1)
            {
                return false;
            }
            result = (*list.begin());
            list.erase(list.begin());
            return true;
        }
        return false;
    }

    bool final(vector<string> &list)
    {
        if (list.size() > 0)
        {
            // Send Error Message Here
            return false;
        }
        return true;
    }
}

namespace stringlib
{
    void tokenise(std::string text, std::vector<std::string> &tokenList, std::string delimiters, std::string quotes, char escape, bool stripQuotes)
    {
        tokenList.clear();
        string::size_type tokenStart, tokenEnd, quoteStart, quoteEnd;
        tokenStart = text.find_first_not_of(delimiters);
        while (tokenStart != string::npos)
        {
            tokenEnd = text.find_first_of(delimiters, tokenStart);
            quoteStart = text.find_first_of(quotes, tokenStart);
            while (quoteStart != string::npos && quoteStart < tokenEnd && quoteStart < text.length())
            {
                string quoteUsed = text.substr(quoteStart, 1);
                quoteEnd = text.find_first_of(quoteUsed, quoteStart + 1);
                while (quoteEnd != string::npos && text.at(quoteEnd - 1) == escape && text.at(quoteEnd - 2) != escape)
                {
                    if (quoteEnd == text.length())
                        break;
                    quoteEnd = text.find_first_of(quoteUsed, quoteEnd + 1);
                }
                // To prevent an error regarding a non-existent (quoteEnd + 1)
                if (quoteEnd == string::npos || quoteEnd == text.length())
                {
                    tokenEnd = string::npos;
                    break;
                }
                tokenEnd = text.find_first_of(delimiters, quoteEnd + 1);
                quoteStart = text.find_first_of(quotes, quoteEnd + 1);
            }
            if (tokenEnd != string::npos)
            {
                string token = text.substr(tokenStart, tokenEnd - tokenStart);
                if (stripQuotes)
                    stripQuoteMarks(token);
                tokenList.push_back(token);
                tokenStart = text.find_first_not_of(delimiters, tokenEnd);
                continue;
            }
            else
            {
                tokenList.push_back(text.substr(tokenStart));
                break;
            }
        }
    }

    void stripQuoteMarks(std::string &text, std::string quotes, char escape)
    {
        string::size_type start = text.find_first_of(quotes);
        if (start == string::npos)
            return;
        while (start > 0 && text.at(start - 1) == escape && (start == 1 || text.at(start - 2) != escape))
        {
            if (start == string::npos && start < text.length())
                return;
            start = text.find_first_of(quotes, start + 1);
        }
        string quoteUsed = text.substr(start, 1);
        string::size_type end = text.find_last_of(quoteUsed);
        while (end > 0 && text.at(end - 1) == escape && (end == 1 || text.at(end - 2) != escape))
        {
            end = text.find_last_of(quotes, end - 1);
        }
        if (end != start)
            text = text.substr(start + 1, end - start - 1);
    }

    void escapeText(std::string &text)
    {
        unsigned long int i = 0;
        string replace;
        while (i < text.length())
        {
            switch (text.at(i + 1))
            {
            case '\'':
                replace = "\\\'";
                break;
            case '\"':
                replace = "\\\"";
                break;
            case '\\':
                replace = "\\\\";
                break;
            case '\a':
                replace = "\\a";
                break;
            case '\n':
                replace = "\\n";
                break;
            case '\r':
                replace = "\\r";
                break;
            case '\t':
                replace = "\\t";
                break;
            case '\v':
                replace = "\\v";
                break;
            }
            if (replace != "")
            {
                string after;
                if (i < text.length())
                    after = text.substr(i + 1);
                text = text.substr(0, i).append(replace).append(after);
                replace = "";
                // Extra i++ to compensate for the added character.
                i++;
            }
            i++;
        }
    }

    void unescapeText(std::string &text)
    {
        unsigned long int i = 0;
        string replace;
        while (i < text.length())
        {
            if (text.at(i) == '\\')
            {
                switch (text.at(i + 1))
                {
                case '\'':
                    replace = "\'";
                    break;
                case '\"':
                    replace = "\"";
                    break;
                case '\\':
                    replace = "\\";
                    break;
                case 'a':
                    replace = "\a";
                    break;
                case 'n':
                    replace = "\n";
                    break;
                case 'r':
                    replace = "\r";
                    break;
                case 't':
                    replace = "\t";
                    break;
                case 'v':
                    replace = "\v";
                    break;
                }
                string after;
                if (i + 1 < text.length())
                    after = text.substr(i + 2);
                text = text.substr(0, i).append(replace).append(after);
            }
            i++;
        }
    }
}
